package edu.cornell.cs4740.wsd;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import edu.cornell.cs4740.wsd.parsing.TrainingData;

public class EvaluationTools {
	
	// uses the performance metric used by kaggle
	public static double[] getKagglePerformances(List<Map<String, List<List<Boolean>>>> predictions, Map<String, List<TrainingData>> actual) {
		double[] performances = new double[predictions.size()];
		Map<String, List<List<Boolean>>> currentPredictionSet;
		
		for(int x = 0; x < performances.length; x++) {
			currentPredictionSet = predictions.get(x);
			double numErrors = 0;
			double total = 0;
			
			for(String word : currentPredictionSet.keySet()) {
				if(actual.containsKey(word)) {

					for(int y = 0; y < actual.get(word).size(); y++) {
						// get the number of errors
						List<Boolean> actualSenses = actual.get(word).get(y).getSenseUsage();
						List<Boolean> currentPreds = currentPredictionSet.get(word).get(y);
						
						for(int j = 0; j < actualSenses.size(); j++) {
							if(!(actualSenses.get(j).equals(currentPreds.get(j)))) {
								numErrors++;
							}
							total++;
						}
					}
				}
			}
			
			performances[x] = numErrors/total;
		}
		
		return performances;
	}
	
	public static double[] getPerformances(List<Map<String, List<List<Boolean>>>> predictions, Map<String, List<TrainingData>> actual){
		double[] performances = new double[predictions.size()];
		Map<String, List<List<Boolean>>> currentPredictionSet;
		double runningSum;
		int count;
		for(int i=0;i<performances.length;i++){
			currentPredictionSet = predictions.get(i);
			runningSum=0;
			count=0;
			for(String word : currentPredictionSet.keySet()){
				if(actual.containsKey(word)){
					for(int j=0;j<actual.get(word).size();j++){
						runningSum+=comparePredictionsToActual(actual.get(word).get(j), currentPredictionSet.get(word).get(j));
						count++;
					}
				}
			}
			
			performances[i]=runningSum/count;
		}
		
		return performances;
	}
	
	public static double[] averageOverFolds(double[][] performances){
		double[] avgs = new double[performances[0].length];
		for(int j=0;j<avgs.length;j++){
			avgs[j]=0;
		}
		for(int i=0;i<performances.length;i++){
			for(int j=0;j<avgs.length;j++){
				avgs[j]+=performances[i][j];
			}
		}
		for(int j=0;j<avgs.length;j++){
			avgs[j]/=performances.length;
		}
		return avgs;
	}
	
	//Smaller number means prediction is more correct
	public static double comparePredictionsToActual(TrainingData actual, List<Boolean> predictions){
		List<Double> actualVec = convertSensesToNormalizedVector(actual.getSenseUsage());
		List<Double> predictVec = convertSensesToNormalizedVector(predictions);
		return distBetweenVectors(actualVec, predictVec);
	}

	public static List<Double> convertSensesToNormalizedVector(List<Boolean> senses){
		List<Double> vec = new ArrayList<Double>();
		double runningSum=0;
		double temp;
		for(Boolean b:senses){
			temp = b?1:0;
			runningSum+=temp;
		}
		double divBy = Math.sqrt(runningSum);
		for(Boolean b:senses){
			temp = b?1.0/divBy:0;
			vec.add(temp);
		}
		return vec;
	}
	
	public static double distBetweenVectors(List<Double> a, List<Double> b){
		//sanity check
		if(a.size()!=b.size()){System.out.println("Can't get distance between vectors of different dimensions");return -1;}
		
		double runningSum=0;
		for(int i=0;i<a.size();i++){
			runningSum+=Math.pow((a.get(i)-b.get(i)),2);
		}
		return Math.sqrt(runningSum);
	}
}
